import uuid from "src/utils/uuid"
import * as path from 'path'
import * as Nedb from 'nedb'

export class NedbModel {
  _id: string | number
  createTime: number = new Date().getTime()
  updateTime: number

  constructor (id?: string | number) {
    if (!id) {
      this._id = uuid()
    } else {
      this._id = id
    }
  }
}

export class NedbDao<T extends NedbModel> {

  private nedb: Nedb
  private compactRateForSave: number = 10000
  private actionCount: number = 0

  constructor (dbDir: string) {
    const dbFile = path.join(dbDir, '/', this.constructor.name, '.db')
    this.nedb = new Nedb({
      filename: dbFile,
      autoload: true
    })
    this.nedb.loadDatabase()
    this.nedb.persistence.compactDatafile()
  }

  private afterAction() {
    this.actionCount++
    if (this.actionCount >= this.compactRateForSave) {
      this.nedb.persistence.compactDatafile()
      this.actionCount = 0
    }
  }

  async save (item: T): Promise<boolean> {
    return new Promise<boolean>((resolve, reject) => {
      this.nedb.insert(item, (err: Error) => {
        if (err) {
          reject(err)
        } else {
          resolve(true)
          this.afterAction()
        }
      })
    })
  }

  async update(item: T): Promise<boolean> {
    return new Promise<boolean>((resolve, reject) => {
      this.nedb.update({_id: item._id}, item, {}, (err: Error) => {
        if (err) {
          reject(err)
        } else {
          resolve(true)
          this.afterAction()
        }
      })
    })
  }

  async findById (id: string, projection: object = {}): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      this.nedb.findOne({_id: id}, projection, (err: Error, doc: T) => {
        if (err) {
          reject(err)
        } else {
          resolve(doc)
        }
      })
    })
  }

  async find (): Promise<T[]> {
    return new Promise<T[]>((resolve, reject) => {
      this.nedb.find('', (err: Error, docs: T[]) => {
        if (err) {
          reject(err)
        } else {
          resolve(docs)
        }
      })
    })
  }
}